﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.SqlCommand;
using Studentium.Entities;
using Studentium.Util;
using TopCoder.Util.Calendar.Entry;

namespace Studentium.UIElements
{
    public partial class EditAttendance : Form
    {
        private ClassAttendance classAttendance;
        private bool newAttendance;
        private StudentInfoTabs studentInfos;

        public EditAttendance(ClassAttendance classAttendance, StudentInfoTabs studentInfos, bool newAttendance)
        {                        
            InitializeComponent();

            this.classAttendance = classAttendance;
            this.newAttendance = newAttendance;
            this.studentInfos = studentInfos;

            commentBox.Text = classAttendance.Comment;            
            datePicker.Value = classAttendance.Date;
            datePicker.Format = DateTimePickerFormat.Custom;
            datePicker.CustomFormat = "dd MMMM yyyy, HH:mm";

            switch (classAttendance.Status)
            {
                case AttendenceStatus.Registered:
                    registeredBtn.Checked = true;
                    break;
                case AttendenceStatus.Attended:
                    attendedBtn.Checked = true;
                    break;
                case AttendenceStatus.WishList:
                    wishlistBtn.Checked = true;
                    break;                    
            }


            UpdateClassesList(false);
            if (classAttendance.CalendarEntry != null)
            {
                classNameBox.SelectedItem = classAttendance.CalendarEntry;
            }
            ReloadClassAttendants();

            datePicker.ValueChanged += datePicker_ValueChanged;
            classNameBox.SelectedIndexChanged += classNameBox_SelectedIndexChanged;
        }

        private void saveBtn_Click(object sender, EventArgs e)
        {
            if (classNameBox.Text.Trim().Length == 0 || classNameBox.SelectedItem == null)
            {
                MessageBox.Show("You should choose class.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if(IsClassAttended((CalendarEntry)classNameBox.SelectedItem))
            {
                DialogResult result =
                    MessageBox.Show("Student is already attending or attended the same class. Proceed anyway?", "Warning",
                                MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (result == DialogResult.No)
                {                    
                    return;
                }
            }

            if (registeredBtn.Checked)
                classAttendance.Status = AttendenceStatus.Registered;
            else
                if (attendedBtn.Checked)
                    classAttendance.Status = AttendenceStatus.Attended;
                else
                    if (wishlistBtn.Checked)
                        classAttendance.Status = AttendenceStatus.WishList;

            if (newAttendance && classAttendance.Student.CalcBoughtClasses() <= classAttendance.Student.CalcWastedClasses())
            {
                DialogResult result = MessageBox.Show("Student has already used all his/her prepaid classes. Proceed anyway?", "Warning",
                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (result == DialogResult.No)
                {
                    return;
                } 
            }

            if ( classAttendance.Status != AttendenceStatus.WishList 
                && IsTooManyStudents((CalendarEntry)classNameBox.SelectedItem))
            {
                DialogResult result = MessageBox.Show("Too many students registered  - more or equal to " +
                                ((CalendarEntry)classNameBox.SelectedItem).MaximumAttendants + ". Proceed anyway?", "Warning",
                                MessageBoxButtons.YesNo, MessageBoxIcon.Warning);                
                if (result == DialogResult.No)
                {                    
                    return;
                }
            }            
            
            classAttendance.Comment = commentBox.Text;            
            classAttendance.CalendarEntry = (CalendarEntry)classNameBox.SelectedItem;

            if(classAttendance.CalendarEntry.Level != StudentLevel.Unknown
                && classAttendance.Student.Level != StudentLevel.Unknown
                && classAttendance.Student.Level != classAttendance.CalendarEntry.Level )

            {
                DialogResult result = MessageBox.Show("Student and class levels doesn't match each other. Proceed anyway?",
                                                      "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (result == DialogResult.No)
                {
                    return;
                }                
            }


            if (newAttendance)                            
                studentInfos.SaveNewStudentItem(classAttendance);           
            else
                studentInfos.UpdateStudentItem(classAttendance);

            this.Close();
        }

        private void cancelBtn_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void datePicker_ValueChanged(object sender, EventArgs e)
        {
            UpdateClassesList(true);
            ReloadClassAttendants();
        }

        private void UpdateClassesList(bool subscribed)
        {
            try
            {
                if (subscribed)
                    classNameBox.SelectedIndexChanged -= classNameBox_SelectedIndexChanged;

                classNameBox.DataSource = studentInfos.MainController.GetCalendarEntries(datePicker.Value);
                classNameBox.DisplayMember = "DescriptionAndDate";

                if (subscribed)
                    classNameBox.SelectedIndexChanged += classNameBox_SelectedIndexChanged;
            }
            catch (Exception e)
            {
                Log.log("Error while parsing schedule", e);
                MessageBox.Show("Error while parsing schedule. Please contact application developer - dkrasikov@gmail.com",
                                "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                throw;
            }
        }

        private void ReloadClassAttendants()
        {
            attendantsBox.Text = "";
            wishedToAttendBox.Text = "";
            if (classNameBox.SelectedItem == null)
                return;
            using (ISession session = studentInfos.MainController.GetSession())
            {
                IList<ClassAttendance> attendances = session.CreateCriteria(typeof (ClassAttendance)).
                    Add(Expression.Eq("CalendarEntry", classNameBox.SelectedItem)).
                    //SetCacheable(true).
                    List<ClassAttendance>();
                foreach (ClassAttendance attendance in attendances)
                {
                    if (attendance.Status == AttendenceStatus.Registered ||
                        attendance.Status == AttendenceStatus.Attended)
                    {
                        attendantsBox.Text += attendance.Student.FullName + Environment.NewLine;
                    }
                    if (attendance.Status == AttendenceStatus.WishList)
                    {
                        wishedToAttendBox.Text += attendance.Student.FullName + Environment.NewLine;
                    }
                }
            }
        }

        private bool AreNamesEqual(string name1, string name2)
        {
            return Normalize(name1) == Normalize(name2);
        }

        private string Normalize(string name)
        {            
            StringBuilder normalized = new StringBuilder(30);
            foreach (string word in name.Split(' ', '\t', '\n', '\r'))
            {
                if (word.Length == 0)
                    continue;

                if (Char.IsNumber(word[0]))
                    break;

                normalized.Append(word);
            }

            return normalized.ToString().ToUpper();
        }

        private bool IsClassAttended(CalendarEntry scheduleEntry)
        {            
            string fullClassName = scheduleEntry.FullName;
            
            CultureInfo cultureInfo = new CultureInfo("en-US");
            Calendar calendar = cultureInfo.Calendar;            
            CalendarWeekRule weekRule = cultureInfo.DateTimeFormat.CalendarWeekRule;
            DayOfWeek firstDow = cultureInfo.DateTimeFormat.FirstDayOfWeek;

            int weekOfYear = calendar.GetWeekOfYear(datePicker.Value, weekRule, firstDow);

            // if new attendance and the same calendar entry exists - return true
            if (newAttendance)            
                foreach (ClassAttendance attendance in classAttendance.Student.Classes)
                {
                    if (attendance.CalendarEntry != null &&
                        attendance.CalendarEntry.EntryId == scheduleEntry.EntryId)
                        return true;
                }

            return classAttendance.Student.Classes.Any(attendance =>
                                                        weekOfYear == calendar.GetWeekOfYear(attendance.Date, weekRule, firstDow)
                                                        && attendance.CalendarEntry != null
                                                        && attendance.CalendarEntry.EntryId != scheduleEntry.EntryId
                                                        && AreNamesEqual(fullClassName, attendance.FullClassName));
        }

        private bool IsTooManyStudents(CalendarEntry scheduleEntry)
        {
            using (ISession session = studentInfos.MainController.GetSession())
            {
                IList result = session.CreateSQLQuery("select 1 from classattendance attendance "
                + " left join calendarentry calendar on attendance.calendarEntryId=calendar.EntryId"
                + " left join student on student.id=attendance.studentId"
                + " where student.BusinessId<>" + classAttendance.Student.BusinessId
                + " and calendar.EntryId=" + scheduleEntry.EntryId
                + " and (Status = 0 || Status = 1)").List();

                return result.Count >= scheduleEntry.MaximumAttendants;

//                IList<ClassAttendance> attendences = session.CreateCriteria(typeof (ClassAttendance)).
                    //Add(Expression.Eq("CalendarEntry.FullClassName", scheduleEntry.Description)).
                    //Add(Expression.Eq("CalendarEntry.Date", scheduleEntry.Date)).
//                    Add(Expression.Or(Expression.Eq("Status", AttendenceStatus.Registered),
//                                      Expression.Eq("Status", AttendenceStatus.Attended))).
//                    List<ClassAttendance>();
//
//                IList<ClassAttendance> filtered =
//                    new List<ClassAttendance>(
//                        attendences.Where(x => x.Student.BusinessId != classAttendance.Student.BusinessId
//                                               && x.CalendarEntry != null
//                                               && x.CalendarEntry.FullName == scheduleEntry.FullName
//                                               && x.CalendarEntry.Date == scheduleEntry.Date));
//                return filtered.Count() >= scheduleEntry.MaximumAttendants;
            }
        }

        private void classNameBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ReloadClassAttendants();
        }
    }
}
